﻿using System;
using System.Collections.Generic;
using System.Linq;

using Duality;
using Duality.Resources;
using Duality.ColorFormat;

using OpenTK;

namespace ClientApp
{
	public enum PlayerIdentity
	{
		Unknown,

		Cherry,
		Lemon
	}
	public static class ExtMethodsPlayerIdentity
	{
		public static Player GetPlayer(this PlayerIdentity id)
		{
			return id == PlayerIdentity.Cherry ? Player.Cherry : Player.Lemon;
		}
		public static PlayerIdentity Opposing(this PlayerIdentity id)
		{
			return id == PlayerIdentity.Cherry ? PlayerIdentity.Lemon : PlayerIdentity.Cherry;
		}
		public static ColorRgba GetUnitColor(this PlayerIdentity id, float hueOffset = 0.0f, float saturation = 1.0f, float brightness = 1.0f)
		{
			float hue = (id == PlayerIdentity.Cherry) ? 350.0f / 360.0f : 46.0f / 360.0f;
			hue = MathF.NormalizeVar(hue + hueOffset, 0.0f, 1.0f);
			return new ColorHsva(hue, saturation, brightness).ToRgba();
		}
	}
    public sealed class Player
    {
        private static Player cherry;
		private	static Player lemon;
		public static Player Cherry
		{
			get
			{
				if (cherry == null) cherry = new Player(PlayerIdentity.Cherry);
				return cherry;
			}
		}
		public static Player Lemon
		{
			get
			{
				if (lemon == null) lemon = new Player(PlayerIdentity.Lemon);
				return lemon;
			}
		}


		private bool				disposed	= false;
		private	PlayerIdentity		id			= PlayerIdentity.Unknown;
		private Headquarter			hq			= null;
		private float				mana		= 1.0f;
		private	WarriorCommand[]	currentCmd	= new WarriorCommand[] { WarriorCommand.Attack, WarriorCommand.Attack, WarriorCommand.Attack };
        [NonSerialized]
        private Dictionary<GestureGroup, Action<float>> gestureMapping = new Dictionary<GestureGroup, Action<float>>();
		[NonSerialized]
		private	List<TuioInstance>[]	handledCursorPaths	= new List<TuioInstance>[] { new List<TuioInstance>(), new List<TuioInstance>(), new List<TuioInstance>() };
		
		public bool Disposed
		{
			get { return this.disposed; }
		}
		public Headquarter Hq
		{
			get { return this.hq; }
			internal set { this.hq = value; }
		}
		public PlayerIdentity Id
		{
			get { return this.id; }
		}
		public float Mana
		{
			get { return this.mana; }
		}
		public WarriorCommand CurrentCmdRock
		{
			get { return this.currentCmd[0]; }
			set { this.currentCmd[0] = value; }
		}
		public WarriorCommand CurrentCmdPaper
		{
			get { return this.currentCmd[1]; }
			set { this.currentCmd[1] = value; }
		}
		public WarriorCommand CurrentCmdScissor
		{
			get { return this.currentCmd[2]; }
			set { this.currentCmd[2] = value; }
		}
		public bool CommandingRock
		{
			get { return this.handledCursorPaths[0].Count > 0; }
		}
		public bool CommandingPaper
		{
			get { return this.handledCursorPaths[1].Count > 0; }
		}
		public bool CommandingScissor
		{
			get { return this.handledCursorPaths[2].Count > 0; }
		}

		private Player(PlayerIdentity id)
		{
			this.id = id;
            this.gestureMapping[PlayerGestures.Rock] = this.SpawnRock;
            this.gestureMapping[PlayerGestures.Paper] = this.SpawnPaper;
            this.gestureMapping[PlayerGestures.Scissor] = this.SpawnScissor;
            this.gestureMapping[PlayerGestures.Attack] = this.CommandAttack;
            this.gestureMapping[PlayerGestures.Retreat] = this.CommandRetreat;
            this.gestureMapping[PlayerGestures.Hold] = this.CommandHold;
			TuioListener.Instance.RemoveTuioCursor += this.Instance_RemoveTuioCursor;
			TuioListener.Instance.UpdateTuioCursor += this.Instance_UpdateTuioCursor;
		}
		~Player() { this.Dispose(); }
		public void Dispose()
		{
			if (this.disposed) return;

			TuioListener.Instance.RemoveTuioCursor -= this.Instance_RemoveTuioCursor;

			if (cherry == this) cherry = null;
			if (lemon == this) lemon = null;
			this.disposed = true;
		}

		public void Update()
		{
			this.mana = MathF.Clamp(this.mana += 0.001f * Time.TimeMult, 0.0f, 1.0f);
		}
		
		private void Instance_UpdateTuioCursor(object sender, TuioEventArgs e)
		{
			if (this.hq == null || this.hq.Disposed) return;
			if (!e.Instance.Path.Any()) return; // Ignore empty path
			if (this.id == PlayerIdentity.Lemon && e.Instance.Path.First().pos.X <= 0.5f) return; // Ignore wrong player 
			if (this.id == PlayerIdentity.Cherry && e.Instance.Path.First().pos.X >= 0.5f) return; // Ignore wrong player 

			Vector2 screenSize = DualityApp.TargetResolution;
			Rect buttonCmdRock;
			Rect buttonCmdPaper;
			Rect buttonCmdScissor;
			if (this.id == PlayerIdentity.Cherry)
			{
				buttonCmdRock = Rect.AlignTopLeft(210, 10, 100, 100);
				buttonCmdPaper = Rect.AlignTopLeft(110, 10, 100, 100);
				buttonCmdScissor = Rect.AlignTopLeft(10, 10, 100, 100);
			}
			else
			{
				buttonCmdRock = Rect.AlignBottomRight(screenSize.X - 210, screenSize.Y - 10, 100, 100);
				buttonCmdPaper = Rect.AlignBottomRight(screenSize.X - 110, screenSize.Y - 10, 100, 100);
				buttonCmdScissor = Rect.AlignBottomRight(screenSize.X - 10, screenSize.Y - 10, 100, 100);
			}

			TuioInstance.PathNode[] path = e.Instance.Path.ToArray();
			for (int i = 0; i < path.Length; i++)
			{
				Vector2 screenPos = Vector2.Multiply(path[i].pos, screenSize);
				if (buttonCmdRock.Contains(screenPos) && !this.handledCursorPaths[0].Contains(e.Instance)) this.handledCursorPaths[0].Add(e.Instance);
				if (buttonCmdPaper.Contains(screenPos) && !this.handledCursorPaths[1].Contains(e.Instance)) this.handledCursorPaths[1].Add(e.Instance);
				if (buttonCmdScissor.Contains(screenPos) && !this.handledCursorPaths[2].Contains(e.Instance)) this.handledCursorPaths[2].Add(e.Instance);
			}
		}
		private void Instance_RemoveTuioCursor(object sender, TuioEventArgs e)
		{
			if (this.hq == null || this.hq.Disposed) return;
			if (!e.Instance.Path.Any()) return; // Ignore empty path
			if (this.id == PlayerIdentity.Lemon && e.Instance.Path.First().pos.X <= 0.5f) return; // Ignore wrong player 
			if (this.id == PlayerIdentity.Cherry && e.Instance.Path.First().pos.X >= 0.5f) return; // Ignore wrong player 

			// Remove handled instances and return
			bool anyHandled = false;
			for (int i = 0; i < this.handledCursorPaths.Length; i++)
			{
				if (this.handledCursorPaths[i].Contains(e.Instance))
				{
					this.handledCursorPaths[i].Remove(e.Instance);
					anyHandled = true;
				}
			}
			if (anyHandled) return;

			// Accept spell and command gestures
            float refAngle = this.id == PlayerIdentity.Lemon ? -90 : 90;
			Gesture gesture = new Gesture(e.Instance.Path.Select(p => new Vector2(p.pos.X * DualityApp.TargetResolution.X, p.pos.Y * DualityApp.TargetResolution.Y)));
            float matchedScore;
            GestureGroup matched = PlayerGestures.Match(gesture, refAngle, out matchedScore);
            if (matched != null && this.gestureMapping[matched] != null)
                this.gestureMapping[matched](matchedScore);
		}

        private void CommandAttack(float score)
		{
			if (this.CommandingRock) this.CurrentCmdRock = WarriorCommand.Attack;
			if (this.CommandingPaper) this.CurrentCmdPaper = WarriorCommand.Attack;
			if (this.CommandingScissor) this.CurrentCmdScissor = WarriorCommand.Attack;
		}
        private void CommandRetreat(float score)
		{
			if (this.CommandingRock) this.CurrentCmdRock = WarriorCommand.Retreat;
			if (this.CommandingPaper) this.CurrentCmdPaper = WarriorCommand.Retreat;
			if (this.CommandingScissor) this.CurrentCmdScissor = WarriorCommand.Retreat;
		}
        private void CommandHold(float score)
		{
			if (this.CommandingRock) this.CurrentCmdRock = WarriorCommand.Hold;
			if (this.CommandingPaper) this.CurrentCmdPaper = WarriorCommand.Hold;
			if (this.CommandingScissor) this.CurrentCmdScissor = WarriorCommand.Hold;
		}

        private void SpawnRock(float score)
        {
			if (this.mana < 0.5f) return;
			this.mana -= 0.5f;

			int rating = MathF.RoundToInt(6 * score);
            this.hq.SpawnWarriors(GameRes.Data.Gamedata.Rock_Prefab, rating + 1);

			this.CreateSpellComment(rating);
        }
        private void SpawnPaper(float score)
        {
			if (this.mana < 0.5f) return;
			this.mana -= 0.5f;

			int rating = MathF.RoundToInt(6 * score);
            this.hq.SpawnWarriors(GameRes.Data.Gamedata.Paper_Prefab, rating + 1);

			this.CreateSpellComment(rating);
        }
        private void SpawnScissor(float score)
        {
			if (this.mana < 0.5f) return;
			this.mana -= 0.5f;

			int rating = MathF.RoundToInt(6 * score);
            this.hq.SpawnWarriors(GameRes.Data.Gamedata.Scissor_Prefab, rating + 1);

			this.CreateSpellComment(rating);
        }

		private void CreateSpellComment(int rating)
		{
			GameObject spellCommentObj = GameRes.Data.Gamedata.SpellComment_Prefab.Res.Instantiate();
			SpellComment spellComment = spellCommentObj.GetComponent<SpellComment>();
			spellComment.Rating = rating;
			spellComment.PlayerId = this.id;
			if (this.id == PlayerIdentity.Cherry)
			{
				spellCommentObj.Transform.Angle = MathF.RadAngle90;
				spellCommentObj.Transform.Pos = this.hq.GameObj.Transform.Pos + Vector3.UnitX * 150.0f;
			}
			else
			{
				spellCommentObj.Transform.Angle = -MathF.RadAngle90;
				spellCommentObj.Transform.Pos = this.hq.GameObj.Transform.Pos - Vector3.UnitX * 150.0f;
			}

			Scene.Current.Graph.RegisterObjDeep(spellCommentObj);
		}
	}
}
